//--------------------------------------------------------------------------------------------------------
// 
// Author: Zachary Gay
// Date: 8/23/2012
// Copyright (c) 2012 Zachary Gay.  All Rights Reserved.
//
// Filename: FileIOController.m
//
//--------------------------------------------------------------------------------------------------------

#import "FileIOController.h"

#import "Card.h"
#import "ManaProducer.h"
#import "ManaGroup.h"
#import "Constants.h"
#import "ManaProducingAbility.h"

// Singleton Instance
static FileIOController *theFileIOController;

// Private Interface
@interface FileIOController ()

- (void) initializeStatLogFile;

@end



@implementation FileIOController

@synthesize deckFileName, statLogFileName;
@synthesize manaTypes;

#pragma mark - Constructor and Singleton Access Methods

- (FileIOController *) initWithDeckFileName:(NSString *)_deckFileName 
                         andStatLogFileName:(NSString *)_statLogFileName
{
    if (self = [super init])
    {
        self.deckFileName = _deckFileName;
        self.statLogFileName = _statLogFileName;
        
        [self initializeStatLogFile];
    }
    
    return self;
}

+ (FileIOController *) theFileIOController
{
    if (theFileIOController == nil)
    {
        NSLog(@"ERROR: Attempted to get -theFileIOController before initialization!");
        return nil;
    }
    
    return theFileIOController;
}

#pragma mark - Reference Methods

- (void) initializeReferences
{
    NSNumber *greenMana = [NSNumber numberWithInt: MANA_TYPE_GREEN];
    NSNumber *blueMana = [NSNumber numberWithInt: MANA_TYPE_BLUE];
    NSNumber *blackMana = [NSNumber numberWithInt: MANA_TYPE_BLACK];
    NSNumber *whiteMana = [NSNumber numberWithInt: MANA_TYPE_WHITE];
    NSNumber *redMana = [NSNumber numberWithInt: MANA_TYPE_RED];
    NSNumber *colorlessMana = [NSNumber numberWithInt: MANA_TYPE_COLORLESS];
    
    self.manaTypes = [[NSDictionary alloc] initWithObjectsAndKeys: 
                      greenMana, @"G", 
                      blueMana, @"U",
                      blackMana, @"B",
                      redMana, @"R",
                      whiteMana, @"W",
                      colorlessMana, @"C",
                      nil];
}

#pragma mark - Stat Log File Methods

- (void) initializeStatLogFile
{
    #warning Implement Me!
    // Check to see if a statLogFile already exists by the given name.  
    // If one does, append our info to it.
    // If it doesn't, create it and add our info to the empty file.
}

- (void) logStatMessage:(NSString *)_statMessage
{
    #warning Implement Me!
    // Append '_statMessage' to the stat log file
}

#pragma mark - Deck File Methods

- (NSArray *) parseNonManaProducerLine: (NSString *) _nonManaProducerLine
{
    // Gather information about the Non Mana Producer
    NSArray *NMPLineParts = [_nonManaProducerLine componentsSeparatedByString: @"_"];
    
    NSString *manaCostString = [NMPLineParts objectAtIndex: 1];
    ManaGroup *manaCost = [self parseManaGroup: manaCostString];
    
    int numberToCreate = [[NMPLineParts objectAtIndex: 2] intValue];
    
    // Create as many as needed
    NSMutableArray *newNonManaProducersCreated = [[NSMutableArray alloc] init];
    for (int i = 0; i < numberToCreate; i++)
    {
        Card *newNonManaProducer = [[Card alloc] initWithManaCost: manaCost];
        [newNonManaProducersCreated addObject: newNonManaProducer];
    }
    
    return newNonManaProducersCreated;
}

- (ManaGroup *) parseManaGroup: (NSString *) _manaGroupString
{
    // Iterate over the characters in the string one by one
    // and create the produceable Mana Group as we go
    BOOL currentlyParsingHybridMana = NO;
    NSMutableArray *singleManasCreated = [[NSMutableArray alloc] init];
    NSMutableArray *hybridManasCreated = [[NSMutableArray alloc] init];
    
    NSMutableArray *currentHybridMana = nil;
    for (int i = 0; i < _manaGroupString.length; i++)
    {
        NSString *currentChar = [_manaGroupString substringWithRange: NSMakeRange(i, 1)];
        
        // If it's a mana type, we can use the mana type reference
        NSNumber *manaType = [manaTypes objectForKey: currentChar];
        if (manaType != nil)
        {
            NSNumber *newMana = [NSNumber numberWithInt: [manaType intValue]];
            if (currentlyParsingHybridMana)
            {
                [currentHybridMana addObject: newMana];
            }
            else 
            {
                [singleManasCreated addObject: newMana];
            }
            
            continue;
        }
        
        // The '{' and '}' characters have special meanings and are dealt with seperately.
        if ([currentChar isEqualToString: @"{"])
        {
            // The start of a hybrid mana
            if (currentlyParsingHybridMana)
            {
                NSLog(@"ERROR: Found out of place '{' character in mana group string!");
                return nil;
            }
            
            currentlyParsingHybridMana = YES;
            currentHybridMana = [[NSMutableArray alloc] init];
        }
        else if ([currentChar isEqualToString: @"}"])
        {
            // The end of a hybrid mana
            if (!currentlyParsingHybridMana)
            {
                NSLog(@"ERROR: Found out of place '}' character in mana group string!");
                return nil;
            }
            
            currentlyParsingHybridMana = NO;
            
            if (currentHybridMana.count != 2)
            {
                NSLog(@"ERROR: Too many manas in hybrid mana cost on line: %@", _manaGroupString);
                return nil;
            }
            else 
            {
                NSNumber *firstMana = [NSNumber numberWithInt: [[currentHybridMana objectAtIndex: 0] intValue]];
                NSNumber *secondMana = [NSNumber numberWithInt: [[currentHybridMana objectAtIndex: 1] intValue]];
                [hybridManasCreated addObject: [NSArray arrayWithObjects: firstMana, secondMana, nil]];
            }
        }
        else 
        {
            NSLog(@"ERROR: Unkown character %@ found when parsing mana ability!", currentChar);
        }
    }

    return [[ManaGroup alloc] initWithSingleManas: singleManasCreated andHybridManas: hybridManasCreated];
}

- (ManaProducingAbility *) parseSingleManaProducingAbilityString: (NSString *) _manaProducingAbilityString
{
    // Check to see if there's a preceeding T which would denote the need to tap when this ability is used.
    BOOL requiresTapping = NO;
    NSRange firstCharacterRange = NSMakeRange(0, 1);
    NSString *firstCharacter = [_manaProducingAbilityString substringWithRange: firstCharacterRange];
    if ([firstCharacter isEqualToString: @"T"])
    {
        // If one is found, strip it from the string so as not to confuse
        // the mana group parsing method
        requiresTapping = YES;
        
        NSMutableString *mutableManaProducingAbilityString = (NSMutableString *)_manaProducingAbilityString;
        [mutableManaProducingAbilityString deleteCharactersInRange: firstCharacterRange];
        
        _manaProducingAbilityString = (NSString *)mutableManaProducingAbilityString;
    }
    
    ManaProducingAbility *newManaProducingAbility = nil;
    ManaGroup *produceableMana = [self parseManaGroup: _manaProducingAbilityString];
    if (produceableMana != nil)
    {
        newManaProducingAbility = [[ManaProducingAbility alloc] initWithProduceableMana: produceableMana 
                                                                        requiresTapping: requiresTapping];
    }
    
    return newManaProducingAbility;
}

- (NSArray *) parseManaProducingAbilitiesString: (NSString *) _manaProducingAbilities
{
    NSMutableArray *newManaAbilitiesCreated = [[NSMutableArray alloc] init];
    
    NSArray *manaAbilities = [_manaProducingAbilities componentsSeparatedByString: @","];
    for (NSString *manaAbilityString in manaAbilities)
    {
        ManaProducingAbility *newManaProducingAbility = [self parseSingleManaProducingAbilityString: manaAbilityString];
        if (newManaProducingAbility != nil)
        {
            [newManaAbilitiesCreated addObject: newManaProducingAbility];
        }
    }
    
    return newManaAbilitiesCreated;
}

- (NSArray *) parseManaProducerLine: (NSString *) _manaProducerLine
{
    // Gather information about the Mana Producer
    BOOL isLand;
    BOOL canBeTappedOnTheFirstTurn;
    ManaGroup *manaCost = nil;
    NSArray *manaProducingAbilities = nil;
    int numberToCreate = 0;
    
    NSArray *MPLineParts = [_manaProducerLine componentsSeparatedByString: @"_"];
    NSString *landOrNonLand = [MPLineParts objectAtIndex: 1];
    if ([landOrNonLand isEqualToString: kLand])
    {
        isLand = YES;
    }
    else 
    {
        isLand = NO;
    }
    
    NSString *canBeTappedOnTheFirstTurnString = [MPLineParts objectAtIndex: 2];
    if ([canBeTappedOnTheFirstTurnString isEqualToString: kCanBeTappedOnFirstTurn])
    {
        canBeTappedOnTheFirstTurn = YES;
    }
    else 
    {
        canBeTappedOnTheFirstTurn = NO;
    }
    
    NSString *manaProducingAbilitiesString = [MPLineParts objectAtIndex: 3];
    manaProducingAbilities = [self parseManaProducingAbilitiesString: manaProducingAbilitiesString];
    
    if (isLand)
    {
        numberToCreate = [[MPLineParts objectAtIndex: 4] intValue];
    }
    else 
    {
        NSString *manaCostString = [MPLineParts objectAtIndex: 4];
        manaCost = [self parseManaGroup: manaCostString];
        
        numberToCreate = [[MPLineParts objectAtIndex: 5] intValue];
    }
    
    // Create as many as are needed
    NSMutableArray *newManaProducersCreated = [[NSMutableArray alloc] init];
    for (int i = 0; i < numberToCreate; i++)
    {
        ManaProducer *newManaProducer = [[ManaProducer alloc] initWithManaCostOrNil: manaCost 
                                                             manaProducingAbilities: manaProducingAbilities 
                                                             andTappableOnFirstTurn: canBeTappedOnTheFirstTurn];
        
        // Now that the ManaProducer itself is instantiated, we can
        // set up our quick-access property for its MPA's
        for (ManaProducingAbility *aMPA in manaProducingAbilities)
        {
            aMPA.myManaProducer = newManaProducer;
        }
        
        [newManaProducersCreated addObject: newManaProducer];
    }
    
    return newManaProducersCreated;
}

- (NSArray *) readLine: (NSString *) _deckDataFileLine
{
    // Seperate line by underscores which delimit different types of card info.
    // We should get a minimum of 3 types and a maximum of 6
    NSArray *deckDataFileLineParts = [_deckDataFileLine componentsSeparatedByString: @"_"];
    if (deckDataFileLineParts.count < 3 || deckDataFileLineParts.count > 6)
    {
        NSLog(@"ERROR: Attempting to read deck data file %@ failed!  Malformed file!", self.deckFileName);
        return nil;
    }
    
    NSArray *cardsRead = nil;
    NSString *typeOfCards = [deckDataFileLineParts objectAtIndex: 0];
    if ([typeOfCards isEqualToString: kManaProducer])
    {
        cardsRead = [self parseManaProducerLine: _deckDataFileLine];
    }
    else 
    {
        cardsRead = [self parseNonManaProducerLine: _deckDataFileLine];
    }
    
    return cardsRead;
}

- (void) readDeckDataFile
{
    /*
        Deck File Data Structure:
            Each line of the deck data file represents a certain "type" of card as the app sees it.  The app distinguishes a type of card based on a few different things.  First, whether it's a mana producer (MP) or not (NMP).  
     
            If the card is a MP, it then checks to see if it can be used on the first turn (H) or not (NH).  Then, it checks if the card is a land (L) or non-land (NL) if it's an MP.  If it is a land, it has no mana cost, so we then just list it's produceable mana abilities seperated with commas and how many we have of them.  For NL MP, we use the same structure and just add a mana cost.
     
            For NMP's, the structure is simpler.  Each line is simply NMP_ManaCost_N.
     
            Produceable Mana uses different characters for each mana type produced, a pre-fix of "T" to show a requirement of tapping the card to use the ability and curly-braces ( {} ) for abilities that can produce one type or another.  
            
            The characters are mapped as follows:
                R = Red
                B = Black
                U = Blue
                G = Green
                W = White
                C = Colorless
     
            Mana Costs are formed similarly to produceable mana.  They just don't use the brackets.
     
            For example a listing for 10 forests would be: MP_H_L_TG_10
            A listing for 4 Llannawor Elves would be: MP_NH_NL_TG_G_4
            A listing for 3 Elvish Warriors would be: NMP_GG_3
    */
    
    NSString *deckDataFilePath = [NSString stringWithFormat:@"/Users/Zack/Desktop/%@", self.deckFileName];
    if (![[NSFileManager defaultManager] fileExistsAtPath: deckDataFilePath])
    {
        NSLog(@"ERROR: Reading of deck file failed!  File not found!");
        return;
    }
    
    // The text files being read are relatively small and performance isn't really too big of a consideration here, so, I'm going to pull the file into memory all at once.
    NSError *error = nil;
    NSString *deckDataFileContents = [NSString stringWithContentsOfFile: deckDataFilePath 
                                                               encoding: NSUTF8StringEncoding 
                                                                  error: &error];
    
    // Seperate file by lines and parse each one.
    NSArray *linesOfDeckDataFile = [deckDataFileContents componentsSeparatedByCharactersInSet: [NSCharacterSet newlineCharacterSet]];
    NSMutableArray *cardsRead = [[NSMutableArray alloc] init];
    for (NSString *lineOfDeckDataFile in linesOfDeckDataFile)
    {
        NSArray *newCards = [self readLine: lineOfDeckDataFile];
        [cardsRead addObjectsFromArray: newCards];
    }
}

@end
